import os
import string
import sys
import unittest
import errno
from unittest.mock import patch

from ascend_deployer.utils import Validator
from ascend_deployer.utils import get_validated_env

PATH_WHITE_LIST_LIN = string.digits + string.ascii_letters + '~-+_./ '
MAX_PATH_LEN = 4096


class TestGetValidatedEnv(unittest.TestCase):

    @patch('os.getenv')
    def test_env_not_set(self, mock_getenv):
        mock_getenv.return_value = None
        with self.assertRaises(ValueError) as context:
            get_validated_env('TEST_ENV')
        self.assertEqual(str(context.exception), "Environment variable TEST_ENV is not set")

    @patch('os.getenv')
    def test_env_value_not_in_whitelist(self, mock_getenv):
        mock_getenv.return_value = 'invalid_value('
        with self.assertRaises(ValueError) as context:
            get_validated_env('TEST_ENV')
        self.assertEqual(str(context.exception), "The path is invalid. The path can contain only char in '{}'".format(PATH_WHITE_LIST_LIN))

    @patch('os.getenv')
    def test_env_value_too_short(self, mock_getenv):
        mock_getenv.return_value = 'a'
        with self.assertRaises(ValueError) as context:
            get_validated_env('TEST_ENV', min_length=2)
        self.assertEqual(str(context.exception), "Value for TEST_ENV is too short. Minimum length: 2, actual: 1")

    @patch('os.getenv')
    def test_env_value_too_long(self, mock_getenv):
        mock_getenv.return_value = 'a' * (MAX_PATH_LEN + 1)
        with self.assertRaises(ValueError) as context:
            get_validated_env('TEST_ENV', max_length=MAX_PATH_LEN)
        self.assertEqual(str(context.exception), "Value for TEST_ENV is too long. Maximum length: {}, actual: {}".format(MAX_PATH_LEN, MAX_PATH_LEN + 1))

    @patch('os.getenv')
    @patch('os.path.lexists')
    @patch('os.path.islink')
    def test_env_value_is_symlink(self, mock_islink, mock_lexists, mock_getenv):
        mock_getenv.return_value = '/path/to/symlink'
        mock_lexists.return_value = True
        mock_islink.return_value = True
        with self.assertRaises(ValueError) as context:
            get_validated_env('TEST_ENV', check_symlink=True)
        self.assertEqual(str(context.exception), "Path for TEST_ENV is a symlink: /path/to/symlink. Symlinks are not allowed for security reasons.")

    @patch('os.getenv')
    @patch('os.path.lexists')
    @patch('os.path.islink')
    def test_env_value_is_not_symlink(self, mock_islink, mock_lexists, mock_getenv):
        mock_getenv.return_value = '/path/to/file'
        mock_lexists.return_value = True
        mock_islink.return_value = False
        self.assertEqual(get_validated_env('TEST_ENV', check_symlink=True), '/path/to/file')

    @patch('os.getenv')
    @patch('os.path.lexists')
    def test_env_value_lexists_error(self, mock_lexists, mock_getenv):
        mock_getenv.return_value = '/path/to/file'
        mock_lexists.side_effect = OSError(errno.ENOENT, 'No such file or directory')
        self.assertEqual(get_validated_env('TEST_ENV', check_symlink=True), '/path/to/file')

    @patch('os.getenv')
    @patch('os.path.lexists')
    def test_env_value_lexists_io_error(self, mock_lexists, mock_getenv):
        mock_getenv.return_value = '/path/to/file'
        mock_lexists.side_effect = IOError(errno.ENOENT, 'No such file or directory')
        self.assertEqual(get_validated_env('TEST_ENV', check_symlink=True), '/path/to/file')


class TestValidator(unittest.TestCase):

    def test_valid_ip(self):
        validator = Validator()
        self.assertEqual(True, validator.is_valid_ip("192.168.0.1"))
        self.assertEqual(True, validator.is_valid_ip("0.0.0.0"))
        self.assertEqual(True, validator.is_valid_ip("2001:0db8:85a3::8a2e:0370:7334"))
        self.assertEqual(True, validator.is_valid_ip("::1"))
        self.assertEqual(False, validator.is_valid_ip(" "))
        self.assertEqual(False, validator.is_valid_ip("11111111111"))
        self.assertEqual(True, validator.is_valid_ip("localhost"))
        self.assertEqual(True, validator.is_valid_ip("LOCALHOST"))
        

if __name__ == '__main__':
    unittest.main()